﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using MC.Phone.Ads.Utils;
using Microsoft.Phone.Reactive;
using Microsoft.Phone.Tasks;

namespace MC.Phone.Ads.Provider
{
    public class GenericAdProviderForXaml : BaseAdProvider
    {
        private const int SecondsBeforeNoAdIsFired = 60;

        public static readonly DependencyProperty XamlUriProperty =
            DependencyProperty.Register("XamlUri", typeof (Uri), typeof (GenericAdProviderForXaml), null);

        public static readonly DependencyProperty OfflineContentProperty =
            DependencyProperty.Register("OfflineContent", typeof (object), typeof (GenericAdProviderForXaml), null);

        private ContentControl _currentAdControl;

        private WeakEventListener<GenericAdProviderForXaml, object, RoutedEventArgs> _loadEventListener;

        private readonly IList<WeakEventListener<GenericAdProviderForXaml, object, RoutedEventArgs>> _clickListeners =
            new List<WeakEventListener<GenericAdProviderForXaml, object, RoutedEventArgs>>();

        public override string Name
        {
            get { return "GenericAdProviderForXaml"; }
        }

        [Category("Ad")]
        [TypeConverter(typeof (UriTypeConverter))]
        public Uri XamlUri
        {
            get { return (Uri) GetValue(XamlUriProperty); }
            set { SetValue(XamlUriProperty, value); }
        }

        private Uri FormattedXamlUri
        {
            get
            {
                if (XamlUri == null)
                    return XamlUri;
                string uriString = string.Format(XamlUri.ToString(),
                                                 HttpUtility.UrlEncode(PhoneHelper.GetAppAttribute("ProductID")),
                                                 CountryCode);
                return new Uri(uriString);
            }
        }

        [Category("Ad")]
        public object OfflineContent
        {
            get { return GetValue(OfflineContentProperty); }
            set { SetValue(OfflineContentProperty, value); }
        }

        public override FrameworkElement ProvideAd()
        {
            _currentAdControl = new ContentControl();
            if (!DesignerProperties.IsInDesignTool)
            {
                _loadEventListener =
                    new WeakEventListener<GenericAdProviderForXaml, object, RoutedEventArgs>(this)
                        {
                            OnEventAction = (instance, source, args) => instance.TryDownloading(),
                            OnDetachAction = weak => _currentAdControl.Loaded -= weak.OnEvent
                        };
                _currentAdControl.Loaded += _loadEventListener.OnEvent;
            }
            return _currentAdControl;
        }

        public override void Clean()
        {
        }

        private void TryDownloading()
        {
            if (FormattedXamlUri != null)
            {
                var wc = new WebClient();
                wc.DownloadStringCompleted += OnDownloadStringCompleted;
                wc.DownloadStringAsync(FormattedXamlUri);
            }
            else
            {
                Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " ShowOfflineContent from TryDownloading");
                ShowOfflineContent();
                OnNewAd();
            }
        }

        private void OnError()
        {
            Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " ShowOfflineContent from OnError");
            ShowOfflineContent();
            OnNewAd();
        }

        private void ShowOfflineContent()
        {
            Dispatcher.BeginInvoke(() =>
                                       {
                                           if (_currentAdControl == null)
                                               return;
                                           var offlineDependencyObject = OfflineContent as DependencyObject;
                                           if (offlineDependencyObject != null)
                                               ModifyHyperlinksInContent(offlineDependencyObject);
                                           var elementContent = (FrameworkElement)OfflineContent;
                                           if (elementContent.Parent is ContentControl)
                                               (elementContent.Parent as ContentControl).Content = null;

                                           _currentAdControl.Content = OfflineContent;
                                       });
            Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " FireNoAdInSeconds from ShowOfflineContent");
            FireNoAdInSeconds(SecondsBeforeNoAdIsFired);
        }

        private void OnDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error != null || e.Cancelled)
            {
                OnError();
            }
            else
            {
                string xaml = e.Result;
                Dispatcher.BeginInvoke(() =>
                                           {
                                               try
                                               {
                                                   object o = XamlReader.Load(xaml);
                                                   if (o != null)
                                                   {
                                                       _currentAdControl.Content = o;
                                                       ModifyHyperlinksInContent(_currentAdControl);
                                                       OnNewAd();
                                                   }
                                               }
                                               catch
                                               {
                                                   OnError();
                                               }
                                           });
                Debug.WriteLine(Thread.CurrentThread.ManagedThreadId +
                                " FireNoAdInSeconds from OnDownloadStringCompleted");
                FireNoAdInSeconds(SecondsBeforeNoAdIsFired);
            }
        }

        private void FireNoAdInSeconds(int seconds)
        {
            Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " FireNoAdInSeconds" + seconds);
            Observable.Interval(TimeSpan.FromSeconds(seconds))
                .Take(1)
                .ObserveOnDispatcher()
                .Subscribe(l => OnNoAd());
        }

        private void ModifyHyperlinksInContent(DependencyObject dependencyObject)
        {
            try
            {
                if (dependencyObject is HyperlinkButton)
                {
                    var hyperlinkButton = dependencyObject as HyperlinkButton;
                    hyperlinkButton.Tag = hyperlinkButton.NavigateUri;
                    hyperlinkButton.NavigateUri = null;
                    var clickListener = new WeakEventListener<GenericAdProviderForXaml, object, RoutedEventArgs>(this)
                                            {
                                                OnEventAction = (instance, source, args) => instance.HandleClick(source),
                                                OnDetachAction = weak => hyperlinkButton.Click -= weak.OnEvent
                                            };
                    hyperlinkButton.Click += clickListener.OnEvent;
                    _clickListeners.Add(clickListener);
                }
                if (dependencyObject is ContentPresenter)
                {
                    var contentPresenter = dependencyObject as ContentPresenter;
                    var contentChild = contentPresenter.Content as DependencyObject;
                    if (contentChild != null)
                    {
                        ModifyHyperlinksInContent(contentChild);
                    }
                }
                int count = VisualTreeHelper.GetChildrenCount(dependencyObject);
                if (count > 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, i);
                        ModifyHyperlinksInContent(child);
                    }
                }
            }
            catch (Exception e)
            {
                OnAdError(e);
            }
        }

        private void HandleClick(object sender)
        {
            var uri = ((HyperlinkButton) sender).Tag as Uri;
            if (uri == null)
                return;
            if (uri.Scheme.ToLower() == "http" ||
                uri.Scheme.ToLower() == "https")
            {
                OnAdEngaged();
                HandleWebTask(uri);
            }
            if (uri.Scheme.ToLower() == "marketplace")
            {
                OnAdEngaged();
                HandleMarketplaceTask(uri);
            }
        }


        private void HandleMarketplaceTask(Uri uri)
        {
            if (uri.Host.ToLower() == "search")
            {
                var task = new MarketplaceSearchTask {ContentType = MarketplaceContentType.Applications};
                string localPath = uri.LocalPath.Trim('/');
                if (!string.IsNullOrWhiteSpace(localPath))
                {
                    task.SearchTerms = localPath;
                }
                try
                {
                    task.Show();
                }
// ReSharper disable EmptyGeneralCatchClause
                catch
                {
                    /*Swallow exception, this can happen when the link is tapped twice.*/
                }
// ReSharper restore EmptyGeneralCatchClause
            }
            if (uri.Host.ToLower() == "review")
            {
                var task = new MarketplaceReviewTask();
                try
                {
                    task.Show();
                }
// ReSharper disable EmptyGeneralCatchClause
                catch
                {
                    /*Swallow exception, this can happen when the link is tapped twice.*/
                }
// ReSharper restore EmptyGeneralCatchClause
            }
            if (uri.Host.ToLower() == "detail")
            {
                var task = new MarketplaceDetailTask {ContentType = MarketplaceContentType.Applications};
                string localPath = uri.LocalPath.Trim('/');
                if (!string.IsNullOrWhiteSpace(localPath))
                {
                    task.ContentIdentifier = localPath;
                }
                try
                {
                    task.Show();
                }
// ReSharper disable EmptyGeneralCatchClause
                catch
                {
                    /*Swallow exception, this can happen when the link is tapped twice.*/
                }
// ReSharper restore EmptyGeneralCatchClause
            }
        }

        private static void HandleWebTask(Uri uri)
        {
            var task = new WebBrowserTask {Uri = uri};
            try
            {
                task.Show();
            } 
// ReSharper disable EmptyGeneralCatchClause
            catch
            {
                /*Swallow exception, this can happen when the link is tapped twice.*/
            }
// ReSharper restore EmptyGeneralCatchClause
        }


#if DEBUG
        ~GenericAdProviderForXaml()
        {
            Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + "--Finalizing " + GetType().FullName);
        }
#endif
    }
}